package org.hackystat.sensor.ant.jdepend;

import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.datatype.XMLGregorianCalendar;

import org.apache.tools.ant.BuildException;
import org.hackystat.sensor.ant.jdepend.jaxb.JDepend;
import org.hackystat.sensor.ant.task.HackystatSensorTask;
import org.hackystat.utilities.tstamp.Tstamp;
import org.hackystat.sensor.ant.jdepend.jaxb.Package;

/**
 * Implements an Ant task that parses the XML files generated by JDepend. The Ant Task sends the
 * Coupling data to a Hackystat server.
 * 
 * @author Philip Johnson
 */
public class JDependSensor extends HackystatSensorTask {

  /** The name of this tool. */
  private static String tool = "JDepend";

  /** Initialize a new instance of a JdependSensor. */
  public JDependSensor() {
    super(tool);
  }

  /**
   * Initialize a new instance of a JDependSensor for testing purposes.
   * 
   * @param host The SensorBase host URL.
   * @param email The SensorBase email to use.
   * @param password The SensorBase password to use.
   */
  public JDependSensor(String host, String email, String password) {
    super(host, email, password, tool);
  }

  /**
   * Parses the JDepend XML file and sends the resulting Coupling data to the SensorBase server.
   * 
   * @throws BuildException If there is an error.
   */
  @Override
  public void executeInternal() throws BuildException {
    this.setupSensorShell();
    int numberOfEntries = 0;
    Date startTime = new Date();
    for (File dataFile : getDataFiles()) {
      try {
        verboseInfo("Processing JDepend file: " + dataFile);
        numberOfEntries += this.processJDependXmlFile(dataFile);
      }
      catch (Exception e) {
        signalError("Failure processing: " + dataFile, e);
      }
    }
    // We've collected the data, now send it. 
    this.sendAndQuit();
    summaryInfo(startTime, "Coupling", numberOfEntries);
  }

  /**
   * Processes the JDepend XML data file, generating sensor data.
   * 
   * @param xmlFile The file containing the JavaNCSS data.
   * @return The number of FileMetrics instances generated.
   * @throws BuildException If problems occur.
   */
  int processJDependXmlFile(File xmlFile) throws BuildException {
    // The start time for all entries will be approximated by the XML file's last mod time.
    // Use the TstampSet to make it unique.
    long startTime = xmlFile.lastModified();
    int count = 0;
    try {
      JAXBContext context = JAXBContext
          .newInstance(org.hackystat.sensor.ant.jdepend.jaxb.ObjectFactory.class);
      Unmarshaller unmarshaller = context.createUnmarshaller();

      // JDepend report.
      JDepend jdepend = (JDepend) unmarshaller.unmarshal(xmlFile);
      // Construct the mapping from Java file paths to their CCN data.
      Package2Path package2path = new Package2Path(getSourceFiles());
      List<Package> packageElements = new ArrayList<Package>();
      if ((jdepend.getPackages() != null) &&
          (jdepend.getPackages().getPackage() != null)) {
        packageElements = jdepend.getPackages().getPackage();
      }

      for (Package packageElement : packageElements) {
        String resource = package2path.getPath(packageElement.getName());
        if ((packageElement.getStats() != null) &&  (resource != null)) {
          long tstamp = this.tstampSet.getUniqueTstamp(startTime);
          XMLGregorianCalendar tstampXml = Tstamp.makeTimestamp(tstamp);
          XMLGregorianCalendar runtimeXml = Tstamp.makeTimestamp(this.runtime);
          // Create the sensor data instance key/value map.
          Map<String, String> keyValMap = new HashMap<String, String>();
          // Required for all sensor data
          keyValMap.put("Tool", tool);
          keyValMap.put("SensorDataType", "Coupling");
          keyValMap.put("Runtime", runtimeXml.toString());
          keyValMap.put("Timestamp", tstampXml.toString());
          keyValMap.put("Resource", resource);
          // Expected for "Coupling" sensor data. 
          keyValMap.put("Type", "package");
          keyValMap.put("Afferent", String.valueOf(packageElement.getStats().getCa().intValue())); 
          keyValMap.put("Efferent", String.valueOf(packageElement.getStats().getCe().intValue())); 
          // add data to sensorshell
          this.sensorShell.add(keyValMap);
          count++;
        }
      }
      return count;
    }
    catch (Throwable e) {
      throw new BuildException(errMsgPrefix + "Failure: " + e.getMessage(), e);
    }
  }
}
